/******************************************************************************
 * @file   :  item16.cpp
 * @brief  :  Know how to pass vector and string data to legacy C APIs
 * Created :  09/15/2010 11:16:15 PM
 * Revision:  none
 * Compiler:  gcc -Wall -Wextra item16.cpp
 *
 * @author :  coanor, coanor@gmail.com
 ******************************************************************************/

#include <vector>
#include <string>
#include <deque>
#include <list>
#include <map>
#include <set>
#include <cstring>

#include <iostream>

using namespace std;

/*
 * For vector, you can do this:
 */
void do_sth(const int* ints, size_t num)
{}

void example1()
{
	vector<int> v;
	do_sth(&(v[0]), v.size());
}

/*
 * For string, you can NOT do like vector, because:
 * 1) the data for strings are not guaranteed to be stored in contiguous memory;
 * 2) the internal representation of a string is not guaranteed to end with a null
 *    character, this explains the existence of the string member function c_str,
 *    which returns a pointer to the value of the string in a form designed for C.
 *    c_str() will return a pointer to a null character, it also works if the string
 *    has embedded nulls, if it does, however, do_sth_on_string is likely to interpret
 *    the first embedded null as the end of the string, string objects do NOT care if
 *    they contain null characters, but char*-based C APIs do.
 */
void do_sth_on_string(const char* str)
{}

void example2()
{
	string str;
	do_sth_on_string(str.c_str());
}

/* The pointers being passed are pointers to const, the vector or string data are
 * being passed to an API that will read it, NOT modify it.
 *
 * For string, there is no guarantee that c_str yields a pointer to the internal
 * representation of the string data: it could return a pointer to an unmodifiable
 * copy of the string's data, one's that's correctly formatted for a C API.
 *
 * For a vector, if you pass v to a C API that modifies v's elements, that's TYPICALLY
 * okay, but the called routine must not attempt to change the number of elements in
 * the vector. For example, it must create new elements in a vector's unused capacity,
 * if it does, v will become unternally inconsistent, because it won't know its
 * correct size any longer, v.size() will yield incorrect results, and if the called
 * routine attempts to add data to a vector whose size and capacity are the same,
 * truly horrible things could happen.
 *
 * Some vectors have extra constraints on their data, and if you pass a vector to
 * an API that modifies the vector's data, you must ensure that the additional constraints
 * continue to be satisfied, for example, item 23 explains how sorted vectors can
 * often be a viable alternative to associative containers, but it's important
 * for such vectors to remain sorted, if you pass a sorted vectors to an API that
 * may modify the vector's data, you will need to take into account that the vector
 * may no longer be sorted after the call has returned.
 */

/* If you have a vector that you would like to initialize with elements from a C
 * API, you can take advantage of the underlying layout compatibility of vectors
 * and arrays by pass to the API the storage for the vector's elements.
 *
 * This technique works only fir vectors, because only vectors are guaranteed to
 * have the same underlying memory layout as arrays, if you want to initialize a
 * string with data from a C API, you can do it easily enough, just have the API
 * put the data into a vector<char>, then copy the data from the vector to the string.
 */
size_t fill_array(double *array, size_t array_size){}
void example3()
{
	int max_num_double = 1024;
	vector<double> vd(max_num_double);
	vd.resize(fill_array(&vd[0], vd.size()));
}

size_t fill_string(char *array, size_t array_size){}
void example4()
{
	int max_num_char = 1024;
	vector<char> vc(max_num_char);
	size_t char_written = fill_string(&vc[0], vc.size());
	string s(vc.begin(), vc.begin() + char_written);
}

/* In fact, the idea of having C API put data into a vector and then copy
 * the data into the STL container you really want it in always works.
 */
void example5()
{
	int max_num_double = 1024;
	vector<double> vd(max_num_double);
	vd.resize(fill_array(&vd[0], vd.size()));
	deque<double> d(vd.begin(), vd.end());		/* copy data into deque */
	list<double> l(vd.begin(), vd.end());		/* copy data into list */
	set<double> s(vd.begin(), vd.end());		/* copy data into set */
}

/* Furthermore, this hints at how STL containers other than vector or
 * string can pass their data to C APIs, just copy each container's data
 * into a vector, then pass it to the API.
 */
void example6()
{
	set<int> int_set;
	vector<int> v(int_set.begin(), int_set.end());	/* copy data into a vector */
	if (!v.empty()) {
		do_sth(&v[0], v.size());
	}
}

/* We use the &v[0] to strings. As we have descussed, string can not use
 * the &v[0] directly.
 */
void example7(char *str)
{
	int len = strlen(str);

	/* test for trimming '\n' while copy into vactor */
	cout << str << endl;
	if (str[len + 1] == '\0') { 
		/* As in MSVC, this will not be true,
		 * so never use &str[0] directly while 
		 * you want your code run every where.
		 */
		string s(str);
		cout << s << endl;
	}
}

int main()
{
	/* test for example7 */	
	string str = "This";

	/* 1) Here, cap == sz == len, it seems that the capacity is dynamically setted.
	 * 2) While use &str[0], debug in gdb reveals that,
	 *        str[4] == str[5] == str[6] == str[7] == '\0'
	 *    and str[8] is a invalid value, can we deem that
	 *        str[size()] ~ str[capacity()]
	 *    are initialized with value '\0'?
	 *    :( We cann't see the bytes between
	 *    :( str[size()] ~ str[capacity()]
	 *    :( in debugging in MSVC. You need to cast the string type to char*, then
	 *    :( the value appears..., Orz
	 * 3) About the capacity():
	 *    While in GCC, it seems that
	 *       size() == length() == capacity(), but in 
	 *    MSVC, the condition is
	 *       size() == length() < capacity()
	 */
	int cap = str.capacity();
	int sz = str.size();
	int len = str.length();
	
	cout << "The size and lenth is " << sz
		<< " and the capacity is " << cap << endl;

	/* While running in MSVC, this will cause nothing been printed,
	 * but GCC will print what you expected.
	 */
	example7(&str[0]);

	/* The correct way is that
	 */
	vector<char> s(str.begin(), str.end());

	/* MUST add the '\0' int MSVC, but GCC do not need if
	 * you want the pefect printing(MSVC) will trim a stranger
	 * character to your printing result.. :(, so C++ is so 
	 * complicated and traps everywhere while you want to make your
	 * code protable..
	 */
	s.push_back('\0');

	example7(&s[0]);

	return 0;
}
